//////////////////////////////////////////////////////////////////////////
#include "UI/Context.hpp"
#include <Rocket/Debugger.h>
#include <LuaBridge/LuaBridge.h>


using namespace UI;


Context::Context()
:	mpContext( nullptr )
,	mMouseMoved( false )
,	mLastMousePos( Math::Point2i::Zero() )
{
}


Context::Context( const std::string& name, const Math::Point2i& size )
:	mName( name )
,	mSize( size )
,	mMouseMoved( false )
,	mLastMousePos( Math::Point2i::Zero() )
{
	mpContext	=	Rocket::Core::CreateContext( name.c_str(), Rocket::Core::Vector2i( size.X(), size.Y() ) );
}


Context::~Context()
{
	if( mpContext!=nullptr )
		mpContext->RemoveReference();
}


bool Context::Initialize( const std::string& name, const Math::Point2i& size, Rocket::Core::RenderInterface* ri )
{
	mName		=	name;
	mSize		=	size;
	mpContext	=	Rocket::Core::CreateContext( name.c_str(), Rocket::Core::Vector2i( size.X(), size.Y() ), ri );

	if( mpContext )
	{
	}

	return mpContext!=nullptr;
}


bool Context::OverUI()
{
	return mpContext->GetHoverElement() != mpContext->GetRootElement();
}


bool Context::ProcessInput( const sf::Event& event )
{
	// Pass mouse move, but don't eat the event.
	if( event.type == sf::Event::MouseMoved )
	{
		mLastMousePos.Set( event.mouseMove.x, event.mouseMove.y );
		mpContext->ProcessMouseMove( event.mouseMove.x, event.mouseMove.y, _GetKeyModifiers() );
		mMouseMoved	=	true;
		return true;
	}

	if( OverUI() )
	{
		switch( event.type )
		{
		case sf::Event::MouseButtonPressed:
			mpContext->ProcessMouseButtonDown( event.mouseButton.button, _GetKeyModifiers() );
			return true;

		case sf::Event::MouseButtonReleased:
			mpContext->ProcessMouseButtonUp( event.mouseButton.button, _GetKeyModifiers() );
			return true;

		case sf::Event::MouseWheelMoved:
			mpContext->ProcessMouseWheel( -event.mouseWheel.delta, _GetKeyModifiers());
			return true;

		case sf::Event::TextEntered:
			if( event.text.unicode > 32 )
			{
				mpContext->ProcessTextInput( event.text.unicode );
				return true;
			}
			break;

		case sf::Event::KeyPressed:
			mpContext->ProcessKeyDown( _TranslateKey( event.key.code), _GetKeyModifiers() );
			return true;

		case sf::Event::KeyReleased:
			mpContext->ProcessKeyUp( _TranslateKey( event.key.code ), _GetKeyModifiers() );
			return true;
		}
	}

	return false;
}


void Context::Update()
{
	if( !mMouseMoved )
	{
		// Always send a mouse event every frame in order to update for
		// program/script moved elements.
		mpContext->ProcessMouseMove( mLastMousePos.X(), mLastMousePos.Y(), _GetKeyModifiers() );
	}
	mMouseMoved	=	false;

	if( mpContext )
		mpContext->Update();
}


void Context::Render()
{
	if( mpContext )
		mpContext->Render();
}


void Context::ClearCache()
{
	Rocket::Core::Factory::ClearStyleSheetCache();
}


Context::RocketElement* Context::GetHoverElement()
{
	return mpContext->GetHoverElement();
}


Context::RocketDocument* Context::LoadDocument( const std::string& name )
{
	return mpContext->LoadDocument( name.c_str() );
}


void Context::UnloadAllDocuments()
{
	mpContext->UnloadAllDocuments();
}


void Context::SetDimensions( uint32_t x, uint32_t y )
{
	mpContext->SetDimensions( Rocket::Core::Vector2i( x, y ) );
}


void Context::InitDebugger() const
{
	Rocket::Debugger::Initialise( mpContext );
}


void Context::Reload( lua_State* state )
{
	std::vector< std::string >	docs;

	for( int i=0; i<mpContext->GetNumDocuments(); ++i )
	{
		Rocket::Core::ElementDocument*	doc	=	mpContext->GetDocument( i );
		docs.push_back( mpContext->GetDocument( i )->GetSourceURL().CString() );
	}

	mpContext->UnloadAllDocuments();
	for( const auto& doc : docs )
	{
		Rocket::Core::ElementDocument*	document	=	mpContext->LoadDocument( doc.c_str() );
		if( document )
		{
			document->Show( Rocket::Core::ElementDocument::NONE );

			// TODO: Handle setting up the global document properly.
			luabridge::LuaRef	docRef( state, document );
			luabridge::setGlobal( state, docRef, "document" );
			document->RemoveReference();
		}
	}

	// TODO: Reset focus properly.
}


/*
Rocket::Core::Context* Context::TheContext() const
{
	return mpContext;
}
*/


//////////////////////////////////////////////////////////////////////////
int Context::_GetKeyModifiers()
{
	int Modifiers = 0;

	if(	sf::Keyboard::isKeyPressed( sf::Keyboard::LShift )		||
		sf::Keyboard::isKeyPressed( sf::Keyboard::RShift )		)
		Modifiers |= Rocket::Core::Input::KM_SHIFT;

	if( sf::Keyboard::isKeyPressed( sf::Keyboard::LControl )	||
		sf::Keyboard::isKeyPressed( sf::Keyboard::RControl )	)
		Modifiers |= Rocket::Core::Input::KM_CTRL;

	if( sf::Keyboard::isKeyPressed( sf::Keyboard::LAlt )		||
		sf::Keyboard::isKeyPressed( sf::Keyboard::RAlt )		)
		Modifiers |= Rocket::Core::Input::KM_ALT;

	return Modifiers;
};


Context::RocketKey_t Context::_TranslateKey( sf::Keyboard::Key Key )
{
	if( Key >= sf::Keyboard::A && Key <= sf::Keyboard::Z )
		return RocketKey_t( int( Rocket::Core::Input::KI_A ) + int( Key-sf::Keyboard::A ) );

	if( Key >= sf::Keyboard::Num0 && Key <= sf::Keyboard::Num9 )
		return RocketKey_t( int( Rocket::Core::Input::KI_0 ) + int( Key-sf::Keyboard::Num0 ) );

	if( Key >= sf::Keyboard::Numpad0 && Key <= sf::Keyboard::Numpad9 )
		return RocketKey_t( int( Rocket::Core::Input::KI_NUMPAD0 ) + int( Key-sf::Keyboard::Numpad0 ) );

	if( Key >= sf::Keyboard::F1 && Key <= sf::Keyboard::F15 )
		return RocketKey_t( int( Rocket::Core::Input::KI_F1 ) + int( Key-sf::Keyboard::F1 ) );

	switch( Key )
	{
	case sf::Keyboard::Left:		return Rocket::Core::Input::KI_LEFT;
	case sf::Keyboard::Right:		return Rocket::Core::Input::KI_RIGHT;
	case sf::Keyboard::Up:			return Rocket::Core::Input::KI_UP;
	case sf::Keyboard::Down:		return Rocket::Core::Input::KI_DOWN;
	case sf::Keyboard::Add:			return Rocket::Core::Input::KI_ADD;
	case sf::Keyboard::BackSpace:	return Rocket::Core::Input::KI_BACK;
	case sf::Keyboard::Delete:		return Rocket::Core::Input::KI_DELETE;
	case sf::Keyboard::Divide:		return Rocket::Core::Input::KI_DIVIDE;
	case sf::Keyboard::End:			return Rocket::Core::Input::KI_END;
	case sf::Keyboard::Escape:		return Rocket::Core::Input::KI_ESCAPE;
	case sf::Keyboard::Home:		return Rocket::Core::Input::KI_HOME;
	case sf::Keyboard::Insert:		return Rocket::Core::Input::KI_INSERT;
	case sf::Keyboard::LControl:	return Rocket::Core::Input::KI_LCONTROL;
	case sf::Keyboard::LShift:		return Rocket::Core::Input::KI_LSHIFT;
	case sf::Keyboard::Multiply:	return Rocket::Core::Input::KI_MULTIPLY;
	case sf::Keyboard::Pause:		return Rocket::Core::Input::KI_PAUSE;
	case sf::Keyboard::RControl:	return Rocket::Core::Input::KI_RCONTROL;
	case sf::Keyboard::Return:		return Rocket::Core::Input::KI_RETURN;
	case sf::Keyboard::RShift:		return Rocket::Core::Input::KI_RSHIFT;
	case sf::Keyboard::Space:		return Rocket::Core::Input::KI_SPACE;
	case sf::Keyboard::Subtract:	return Rocket::Core::Input::KI_SUBTRACT;
	case sf::Keyboard::Tab:			return Rocket::Core::Input::KI_TAB;
	};

	return Rocket::Core::Input::KI_UNKNOWN;
};
